home *** CD-ROM | disk | FTP | other *** search
/ AppleScript - The Beta Release / AppleScript - The Beta Release.iso / Documentation / develop / Apple Event Objects and You / Apple Event Objects (code) / cEntry.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-08  |  17.1 KB  |  490 lines  |  [TEXT/KAHL]

  1.  
  2.  
  3. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  4. /*                                                                                   */
  5. /*    This file contains the code for implementing the cEntry AE object.                */
  6. /*                                                                                   */
  7. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  8.  
  9.  
  10. #include "AEPackObject.h"
  11. #include "AEObjects.h"
  12. #include "AERegistry.h"
  13. #include <packages.h>
  14. #include "ScriptScrap.h"
  15. #include "Prototypes.h"
  16.  
  17.  
  18. /* Object from container accessors */
  19.  
  20. pascal    OSErr EntryFromDocumentAccessor(DescType desiredClass,
  21.             const AEDesc *container, DescType containerClass, DescType keyForm,
  22.             const AEDesc *keyData, AEDesc *value, long LongInt)
  23. {
  24.     long        entryNum;                /* The number contained within the key */
  25.     short        resID, index;            /* Used in creating new tokens */
  26.     OSErr        err     = noErr;        /* Error result code */
  27.     Boolean        getAll;                    /* TRUE if the user asked for all entries */
  28.  
  29.     if (keyForm == formAbsolutePosition) {
  30.         /* This can either an index (position 1, 2, 3), a position relative to the end  */
  31.         /* of the container (-1, -2, -3), or some absolute position ("beginning", "end" */
  32.         /* "middle", etc.).                                                                */
  33.         getAll = false;
  34.         err = Entry_ConvertAbsKey(keyData, &entryNum, &getAll);
  35.         if (err) goto done;
  36.         
  37.         if (getAll) return errAEEventFailed;    /* We don't support "all" */
  38.  
  39.         UseResFile(tokenFRefNum(*container));    /* Make sure we can get to the resource file */
  40.         if (err = ResError()) goto done;
  41.         err = _EntryToScrapIndex(entryNum, &index, &resID);
  42.         if (err) goto done;
  43.         /* Place this information into a token and return the token */
  44.         err = AEDuplicateDesc(container, value);
  45.         if (err) goto done;
  46.         tokenObjectIndex(*value) = entryNum;
  47.         tokenDispatchClass(*value) = cEntry;
  48.         tokenResID(*value) = resID;                /* Since all of the items that comprise this */
  49.                                                 /* entry have the same resource ID, store it here */
  50.         value->descriptorType = cEntry;
  51.     }
  52.     else
  53.         err = errAEBadKeyForm;
  54.  
  55. done:
  56.     return err;
  57. }    /*  EntryFromDocumentAccessor */
  58.  
  59.  
  60.  
  61. /* Property from object accessor */
  62. pascal    OSErr PropertyFromEntryAccessor ( DescType desiredClass,
  63.             const AEDesc *container, DescType containerClass, DescType keyForm,
  64.             const AEDesc *keyData, AEDesc *returnedToken, long refCon )
  65. {
  66.     OSErr        err = noErr;
  67.     ourToken    tokenBody;
  68.     DescType    requestedProperty;
  69.     
  70.     /* This accessor is a little different, as all it does is copy the container */
  71.     /* token and change the token to represent a property.                         */
  72.     /* The actual reading and writing of the data is done in the token handlers. */
  73.     if ((keyForm != formPropertyID) || (keyData->descriptorType != typeType))
  74.         return errAECantSupplyType;
  75.  
  76.     requestedProperty = **(DescType**)keyData->dataHandle;
  77.     
  78.     /* Check to see if this is a legal property code for this object class */
  79.     if ((requestedProperty != pBestType) && (requestedProperty != pClass)
  80.                                             && (requestedProperty != pDefaultType))
  81.         return errAECantSupplyType;
  82.     
  83.     /* It's a legal property code, so duplicate the object's token and indicate */
  84.     /* that we want this token to represent a property.                            */
  85.     err = AEDuplicateDesc(container, returnedToken);
  86.     if (err != noErr) return err;
  87.     tokenFlags(*returnedToken) = kPropToken;
  88.     tokenPropCode(*returnedToken) = requestedProperty;
  89.  
  90.     return noErr;
  91. } /* PropertyFromEntryAccessor */
  92.  
  93.  
  94. OSErr Entry_ConvertAbsKey(const AEDesc *keyData, long *index, Boolean *getAll)
  95. /* This routine extracts the value from a formAbsolutePosition key, including the special */
  96. /* "first", "last", any", "all", and "middle" constants.  */
  97. {
  98.     short     numElements;
  99.     long    rawIndex;
  100.     OSErr    err;
  101.     
  102.     /*  There are 2 flavors of formAbsolutePosition key -- typeLongInteger (1 from the     */
  103.     /* beginning, 2 from the beginning, 1 from the end (-1), 2 from the end (-2), etc.) */
  104.     /* or typeAbsoluteOrdinal (first, last, middle, any, and all). */
  105.     /*  We'll handle each of these forms in turn */
  106.     
  107.     /* We need to initialize some variables */
  108.     err = CountScrapbookEntries(&numElements);
  109.     if (err) return err;
  110.     *getAll = FALSE;
  111.     rawIndex = **((long**)keyData->dataHandle);        /* Get the number out of the key */
  112.     
  113.     /* Handle each of the variations of the formAbsolutePosition key */
  114.     switch (keyData->descriptorType) {
  115.  
  116.         case typeLongInteger:
  117.         case typeIndexDescriptor:
  118.         if (rawIndex < 0)
  119.             /* A negative value means "the Nth object from the end", */
  120.             /* i.e. -1 == the last object */
  121.             rawIndex = numElements + rawIndex + 1;
  122.             /*  Otherwise the number is positive, and a regular index */
  123.         break;
  124.         
  125.         case typeAbsoluteOrdinal:
  126.             /* The key data will contain a 4-byte symbolic constant  */
  127.             /* for the beginning, end, middle, any, or every element */
  128.             if (rawIndex == kAEFirst)
  129.                 rawIndex = 1;
  130.             else if (rawIndex == kAELast)
  131.                 rawIndex = numElements;
  132.             else if (rawIndex == kAEMiddle)
  133.                 rawIndex = numElements / 2;
  134.             else if (rawIndex == kAEAny) {
  135.                 if (numElements <= 1)        /*  0 or 1 elements */
  136.                     rawIndex = numElements;
  137.                 else
  138.                     /* Get a random number between 1 and numElements */
  139.                     rawIndex = 1 + ((unsigned long)Random() * (numElements - 1)) >> 16;
  140.                 }
  141.             else if (rawIndex == kAEAll)
  142.                 *getAll = TRUE;
  143.         break;
  144.     }
  145.     
  146.     /* Make sure the returned value is in range */
  147.     *index = (rawIndex < 1) ? 1: rawIndex;    /*  If rawIndex is 0 or negative, return 1  */
  148.     if (rawIndex > numElements)                /*  Make sure it's not too large either */
  149.         return errAENoSuchObject;
  150.     else
  151.         return noErr;
  152. } /* Entry_ConvertAbsKey */
  153.  
  154.  
  155. /* This is the routine that handles creating a new entry. We may be asked to create */
  156. /* the new element at the beginning or end of the scrapbook, before or after a         */
  157. /* particular entry, or in place of a particular entry.                                */
  158.  
  159. /* We'll return an object specifier for the newly created element */
  160. OSErr Entry_InsertTokenData (const AERecord *insertionLoc,  const AEDescList *data,
  161.                            short *newElementNumber /* returned */)
  162. {
  163.     OSErr        err              = noErr;
  164.     AEDesc        objectSpec      = {'null', 0L};
  165.     AEDesc        resolvedToken = {'null', 0L};
  166.     short        numEntries, entryNum;
  167.     DescType    insertionPos;
  168.     DescType    typeCode;
  169.     long        actualSize;
  170.     Boolean        entryRemoved;
  171.     
  172.     /* The insertionLoc record, which is of type "typeInsertionLoc", contains an object         */
  173.     /* specifier which points to a particular object and an enumerated value which specifies */
  174.     /* where we should create the new element relative to the specified object.                 */
  175.     err = AEGetKeyPtr(insertionLoc, keyAEPosition, typeEnumeration, &typeCode, (Ptr)&insertionPos,
  176.                            sizeof(insertionPos), &actualSize);
  177.     if (err != noErr) goto done;    /* Cleanup and return the error to the caller */
  178.  
  179.     /* Extract and resolve the object specifier */
  180.     err = AEGetKeyDesc(insertionLoc, keyAEObject, typeWildCard, &objectSpec);
  181.     if (err != noErr) goto done;
  182.     if (objectSpec.descriptorType == typeNull)
  183.         return errAENoSuchObject;
  184.     err = AEResolve(&objectSpec, kAEIDoMinimum, &resolvedToken);
  185.     if (err != noErr) goto done;
  186.  
  187.     /* Make sure that the object specifier refers to a document or entry */
  188.     if ((resolvedToken.descriptorType != cWindow) &&
  189.         (resolvedToken.descriptorType != cDocument) &&
  190.         (resolvedToken.descriptorType != cEntry)) {
  191.         err = errAETypeError;
  192.         goto done;
  193.     }
  194.  
  195.     /* "resolvedToken" represents either the entry's container (the scrapbook) or */
  196.     /* another entry. In either case, the token contains the information necesary */
  197.     /* to access the scrapbook file -- we just need to decide on an entry number  */
  198.     /* for the new object, create that entry (if we're not supposed to replace an */
  199.     /* existing entry), and store the data there.                                  */
  200.     
  201.     UseResFile(tokenFRefNum(resolvedToken));
  202.     entryNum = tokenObjectIndex(resolvedToken);
  203.     if (insertionPos == kAEReplace) {
  204.         /* The kAEReplace option says that the new element should completely */
  205.         /* replace ths existing element. The easiest way to do that (in this */
  206.         /* program) is to delete the existing element and then insert the      */
  207.         /* new data into a new element. Since deleting element "n" moves all */
  208.         /* of the following elements down by 1 ("n+1" becomes "n", "n+2"     */
  209.         /* becomes "n+1", etc.) we'll insert our element in front of the new */
  210.         /* element "n".                                                         */
  211.         err = Delete1ScrapbookItem (entryNum, typeWildCard, &entryRemoved);
  212.         if (err != noErr) goto done;
  213.         insertionPos = kAEBefore; 
  214.     }
  215.     
  216.     /* The following switch statement uses the "position" value to calculate the  */
  217.     /* new entry's number.                                                          */    
  218.     switch (insertionPos) {
  219.         case kAEBeginning:
  220.             entryNum = 1;                    /* Insert at the front of the list */
  221.             /* In this case, "resolvedToken" is a token */
  222.             /* for the element's container (i.e. the     */
  223.             /* scrapbook.)                                */
  224.         break;
  225.     
  226.         case kAEEnd:
  227.             entryNum = numEntries + 1;         /* Insert at the end of the list */
  228.             /* In this case, "resolvedToken" is a token */
  229.             /* for the element's container (i.e. the     */
  230.             /* scrapbook.)                                */
  231.         break;
  232.         
  233.         case kAEBefore:                        /* Insert in front of element "entryNum"  */
  234.             /* Our "InsertScrapbookEntries" routine does */
  235.             /* this by default, so we don't have to do    */
  236.             /* anything to entryNum.                    */
  237.             /* The resolvedToken points to the entry     */
  238.             /* which will come after this one.            */
  239.         break;
  240.         
  241.         case kAEAfter:                        /* Insert in front of element "entryNum"+1 */
  242.             entryNum += 1;
  243.             /* The resolvedToken points to the entry     */
  244.             /* which will come before this one.            */
  245.         break;        
  246.     }
  247.     
  248.     /* Create the new element and put the data into it */
  249.     err = InsertScrapbookEntry (entryNum, data);
  250.     if (err == noErr)
  251.         /* Tell the window about the change */
  252.         EntryAdded(tokenWindow(resolvedToken), entryNum);
  253.  
  254. done:
  255.     if (resolvedToken.dataHandle != 0L)
  256.         (void) AEDisposeDesc(&resolvedToken);
  257.     if (objectSpec.dataHandle != 0L)
  258.         (void) AEDisposeDesc(&objectSpec);
  259.     *newElementNumber = entryNum;
  260.     return err;
  261. } /* Entry_InsertTokenData */
  262.  
  263.  
  264. /* This token handler takes a property or object token and returns the data  */
  265. /* represented by that token in a form which is usable to the outside world. */
  266. OSErr Entry_ReadTokenData(const AEDesc *theToken, AEDesc *tokenContents)
  267. {
  268.     AEDescList    allElements    = {typeNull, NULL};
  269.     DescType    propCode, objClass, typeCode;
  270.     OSErr        err            = noErr;
  271.         
  272.     /*  Get the thing pointed to by a property or object token */
  273.     UseResFile(tokenFRefNum(*theToken));
  274.     objClass = tokenDispatchClass(*theToken);
  275.     if (tokenFlags(*theToken) & kPropToken) {
  276.         /* Return any readable properties */
  277.         propCode = tokenPropCode(*theToken);
  278.         
  279.         switch (propCode) {
  280.             case pClass:
  281.                 /* Tell the outside world what class this is */
  282.                 return AECreateDesc(typeType, (Ptr)&objClass, sizeof(objClass), tokenContents);
  283.             break;
  284.  
  285.             case pBestType:
  286.             case pDefaultType:
  287.                 /* Choosing a "best" type and a "default" type for a scrapbook entry */
  288.                 /* presents some real difficulties, since the best type could vary   */
  289.                 /* from entry to entry and there's no one type that can be derived   */
  290.                 /* from every entry (hence, I can't just hardwire a default type.)     */
  291.                 /* We'll solve this for now by scanning the elements until we find     */
  292.                 /* something that we recognize, and will return the type code for      */
  293.                 /* that. If we don't find anything recognizable, we'll return the     */
  294.                 /* type code for the first thing that we see.                         */
  295.                 err = GetBestType(tokenObjectIndex(*theToken), &typeCode);
  296.                 if (err != noErr) return err;
  297.                 return AECreateDesc(typeType, (Ptr)&typeCode, sizeof(typeCode), tokenContents);
  298.             break;
  299.  
  300.         }
  301.     }
  302.  
  303.     if (tokenFlags(*theToken) & kObjectToken) {
  304.         /* If you're ever asked to "read" an object's value, you should return the */
  305.         /* default type. Unfortunately, in the scrapbook, this default type can    */
  306.         /* change from entry to entry (see the long discussion under "pDefaultType"*/
  307.         /* above), so we have to decide what type to use, and then we can get and  */
  308.         /* and return the element with that particular type.                       */
  309.         err = GetBestType(tokenObjectIndex(*theToken), &typeCode);
  310.         if (err != noErr) return err;
  311.         /* typeCode now holds the "best" type for this entry. So, get and return   */
  312.         /* that element.                                                           */
  313.         return Get1ScrapbookItem(tokenObjectIndex(*theToken), typeCode, tokenContents);
  314.     }
  315.     else
  316.         return errAENoSuchObject;
  317. }
  318.  
  319. /* This token handler is used to modify some property of an object. */
  320. OSErr Entry_WriteTokenData(const AEDesc *theToken, const AEDesc *data)
  321. {
  322.     if (tokenFlags(*theToken) & kPropToken)
  323.         /* We don't have any modifiable properties in this class. */
  324.         return errAENotModifiable;
  325.     
  326.     /* Otherwise, we've been handed some data to write to the entry. */
  327.     /* We'll just insert this data as a new element.                 */
  328.     if (data->dataHandle != NULL)
  329.         return Put1ScrapbookItem(tokenObjectIndex(*theToken), data);
  330.     else
  331.         return errAEEventFailed;
  332. }
  333.  
  334.  
  335. /* This token handler is used to delete a particular object. */
  336. /* In this implementation, I've decided to make each object     */
  337. /* responsible for its own creation and destruction, so this */
  338. /* routine will delete a single entry (and all of its         */
  339. /* elements) from the scrapbook.                             */
  340. OSErr Entry_DeleteTokenData(const AEDesc *theToken)
  341. {
  342.     OSErr    err;
  343.     Boolean    entryRemoved;
  344.     
  345.     if (tokenFlags(*theToken) & kPropToken)
  346.         /* Properties may not be deleted */
  347.         return errAENotModifiable;
  348.  
  349.     /* This must be an object token, so get rid of the entire entry */
  350.     UseResFile(tokenFRefNum(*theToken));
  351.     err = Delete1ScrapbookItem(tokenObjectIndex(*theToken), typeWildCard, &entryRemoved);
  352.     if (err == noErr) 
  353.         /* Notify the window that one of the entries is gone */
  354.         EntryRemoved(tokenWindow(*theToken), tokenObjectIndex(*theToken));
  355.     return err;
  356. }
  357.  
  358.  
  359. OSErr Entry_GetData(AppleEvent *message, AppleEvent *reply, long refcon,
  360.                 AEDesc *token, AEDesc *replyObject)
  361. {
  362.     return Entry_ReadTokenData(token, replyObject);
  363. } /* Entry_GetData */
  364.  
  365.  
  366. OSErr Entry_SetData(AppleEvent *message, AppleEvent *reply, long refcon,
  367.                 AEDesc *token, AEDesc *replyObject)
  368. {
  369.     AEDesc    theData;
  370.     OSErr    err;
  371.  
  372.     err = AEGetKeyDesc(message, keyAEData, typeWildCard, &theData);
  373.     if (err == noErr) {
  374.         /*  We don't handle passing in the data as an object specifier */
  375.         if (theData.descriptorType == typeObjectSpecifier) {
  376.             return errAECantHandleClass;
  377.         }
  378.         err = Entry_WriteTokenData(token, &theData);
  379.         (void)AEDisposeDesc(&theData);
  380.         InvalidateWindow(tokenWindow(*token));    /* Redraw the window */
  381.     }
  382.     return err;
  383. } /* Entry_SetData */
  384.  
  385.  
  386. OSErr Entry_CreateElement(AppleEvent *message, AppleEvent *reply, long refcon,
  387.                 AEDesc *token, AEDesc *replyObject)
  388. {
  389.     OSErr        err = noErr;
  390.     AEDesc        data         = {typeNull, NULL};
  391.     AERecord    insertionLoc = {typeNull, NULL};
  392.     short        newEntryNum;
  393.     
  394.     /* Extract the insertionLoc record and have the Apple Event Manager coerce it into    */
  395.     /* an AERecord for us.                                                                 */
  396.     err = AEGetParamDesc(message, keyAEInsertHere, typeAERecord, &insertionLoc);
  397.     if (err != noErr) goto done;
  398.  
  399.     /* Extract the optional data */
  400.     err = AEGetParamDesc(message, keyAEData, typeWildCard, &data);
  401.     /* Since this is an optional parameter, don't exit if the keyword wasn't found */
  402.     if ((err != noErr) && (err != errAEDescNotFound)) goto done;
  403.     
  404.     /* Create the element */
  405.     err = Entry_InsertTokenData(&insertionLoc, &data, &newEntryNum);
  406.     if (err != noErr) goto done;
  407.     /* <<< Construct an object specifier for the new entry and return it here */
  408.  
  409. done:
  410.     if (insertionLoc.dataHandle != 0)
  411.         (void) AEDisposeDesc(&insertionLoc);
  412.         
  413.     if (data.dataHandle != 0)
  414.         (void) AEDisposeDesc(&data);
  415.  
  416.     return err;
  417. } /* Entry_CreateElement */
  418.  
  419.  
  420. OSErr Entry_DeleteElement(AppleEvent *message, AppleEvent *reply, long refcon,
  421.                 AEDesc *token, AEDesc *replyObject)
  422. {
  423.     return Entry_DeleteTokenData(token);
  424. } /* Entry_DeleteElement */
  425.  
  426.  
  427. OSErr Entry_AE_Dispatcher(const AppleEvent *message, AppleEvent *reply, long refCon,
  428.                         AEEventClass classID, AEEventID eventID,
  429.                         const AEDesc *ospec, const AEDesc *token)
  430. {
  431.     OSErr    err = noErr;
  432.     AEDesc    replyDesc = {'null', 0L};
  433.  
  434.     if (classID == kAECoreSuite) {
  435.         switch (eventID) {
  436.             case kAECreateElement:
  437.                 err =  Entry_CreateElement(message, reply, refCon, token, &replyDesc);
  438.             break;
  439.  
  440.             case kAEDelete:
  441.                 err = Entry_DeleteElement(message, reply, refCon, token, &replyDesc);
  442.             break;
  443.  
  444.             case kAEGetData:
  445.                 err =  Entry_GetData(message, reply, refCon, token, &replyDesc);
  446.             break;
  447.  
  448.             case kAEGetDataSize: {
  449.                 /*  We'll do this one by executing "Get Data" and then returning the  */
  450.                 /*  size of the information.  */
  451.                 AEDesc    tempDesc = {'null', NULL};
  452.                 long    dataSize;
  453.                 
  454.                 err = Entry_GetData(message, reply, refCon, token, &tempDesc);
  455.                 if (tempDesc.dataHandle != NULL) {
  456.                     dataSize = GetHandleSize(tempDesc.dataHandle);
  457.                     (void)AECreateDesc(typeLongInteger, (Ptr)&dataSize, sizeof(dataSize), &replyDesc);
  458.                     (void)AEDisposeDesc(&tempDesc);
  459.                 }
  460.             }
  461.             break;
  462.  
  463.             case kAESetData:
  464.                 err =  Entry_SetData(message, reply, refCon, token, &replyDesc);
  465.             break;
  466.  
  467.             /* Events that we don't handle */
  468.             case kAEClone:
  469.             case kAECountElements:
  470.             case kAEDoObjectsExist:
  471.             case kAEGetClassInfo:
  472.             case kAEMove:
  473.             default:
  474.                 err = errAEEventNotHandled ;
  475.         }
  476.     }
  477.     else
  478.         err = errAEEventNotHandled;
  479.  
  480.     /* See if we need to return anything */
  481.     if ((err == noErr) && (replyDesc.descriptorType != 'null')
  482.             && (reply->descriptorType != 'null')) {    /* Note: if the other side didn't ask for a reply, this could be a null desc */
  483.             err = AEPutParamDesc(reply, keyDirectObject, &replyDesc);
  484.             (void)AEDisposeDesc(&replyDesc);
  485.     }
  486.  
  487.     return err;
  488. } /* Entry_AE_Dispatcher */
  489.  
  490.